/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package compilador.analisadorSintatico;

import compilador.analisadorLexico.Token;
import compilador.analisadorLexico.TokenClass;
import compilador.analisadorSintatico.simboloGramatical.ErroSintatico;
import compilador.analisadorSintatico.simboloGramatical.NaoTerminal;
import compilador.analisadorSintatico.simboloGramatical.SimboloGramatical;
import compilador.analisadorSintatico.simboloGramatical.Terminal;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Henrique
 */
public abstract class Producao {

    
    //
    protected SimboloGramatical simbolo;
        
    public Producao() {
        //Chama método que deve ser implementado na interface
        this.setSimbolo();
        
        //DEBUG
        if(AnalisadorSintatico.DEBUG)
            System.out.println("[An.Sintatico] " + this.getClass().getSimpleName() + " " + TokenReader.getInstance().seeCurrent().getAtributo().toString());
    }
    
    
    
    public SimboloGramatical getSimbolo() {
        return simbolo;
    }

    public abstract void setSimbolo();
    
    private static List<Token> tokensSincronizacao = new ArrayList<Token>();
    static{
        tokensSincronizacao.add(new Token(TokenClass.DELIMITADOR, ")", 0));
        tokensSincronizacao.add(new Token(TokenClass.DELIMITADOR, ";", 0));
        //tokensSincronizacao.add(new Token(TokenClass.DELIMITADOR, "]", 0));
    }

    public abstract SimboloGramatical derivar() throws Exception;

    /* caso padrão, o método do Desespero procura por algum token que pertença
     * aos seguintes do não-terminal onde houve o erro
     */ 
    protected void erro(String descricao){
        String nomeSimbolo = this.getClass().getSimpleName();
        
        List<Token> seguintes = TabelaSeguintes.getSeguintes(nomeSimbolo);
        
        erro(descricao, seguintes);
    }
    
    /* caso especifico, o método do Desespero procura por algum token que pertença
     * à lista passada como parametro
     */ 
    protected void erro(String descricao,  List<Token> tokensProcurados){
        ErroSintatico erro = new ErroSintatico();
        erro.setDescricao(descricao);

        erro = metodoDoDesespero(erro, tokensProcurados);
        
        if(this.simbolo instanceof NaoTerminal){
            NaoTerminal nt = (NaoTerminal) this.simbolo;
            nt.add(erro);
        
        }else if(this.simbolo instanceof Terminal){
            this.simbolo = erro;
        }
            
    }

    
    private ErroSintatico metodoDoDesespero(ErroSintatico erro, List<Token> tokensProcurados) {
        final Token seeCurrent = TokenReader.getInstance().seeCurrent();
        //DEBUG
        if(AnalisadorSintatico.DEBUG)
            System.out.print("[Metodo do Desespero] " + this.getClass().getSimpleName() + " " + seeCurrent.getAtributo().toString() + "(" + seeCurrent.getLinha() + ") ");

        
        if(tokensProcurados == null){
            tokensProcurados = new ArrayList<Token>();
        }
        
        tokensProcurados.addAll(tokensSincronizacao);
        
        Token encontrado = null;
        /*encontrado = TokenReader.getInstance().seeCurrent();
        erro.add(encontrado);
        
        if(findToken(encontrado, tokensProcurados)){
            return erro;
        }*/
                         
        while (TokenReader.getInstance().hasToken()) {
            encontrado = TokenReader.getInstance().consome();
            erro.add(encontrado);
            
            if(AnalisadorSintatico.DEBUG)
                System.out.print(encontrado.getAtributo().toString());

            if(findToken(encontrado, tokensProcurados)){
                break;
            }
            
            //erro.add(encontrado);
            //TokenReader.getInstance().consome();
        }
        
        System.out.println();
        
        return erro;
    }
    
    private boolean findToken(Token encontrado, List<Token> tokensProcurados){
        for (Token seg : tokensProcurados) {
            if (encontrado.getTokenClass().equals(seg.getTokenClass())) {
                if (seg.getAtributo() == null) {
                    return true;
                } else if (encontrado.getAtributo().toString().equals(seg.getAtributo())) {
                    return true;
                }
            }
        }
        
        return false;
    }
}
